/**
 * Copyright (c) 2018-2023, NXOS Development Team
 * SPDX-License-Identifier: Apache-2.0
 *
 * Contains: Virtio specification definitions
 *
 * Change Logs:
 * Date           Author            Notes
 * 2023-07-03     Shiroko           Init
 */


#ifndef __DRIVERS_VIRTIO_VIRTIO_H__
#define __DRIVERS_VIRTIO_VIRTIO_H__

#include "virtio_queue.h"

/* VIRTIO Generic Definitions */
#define VIRTIO_STATUS_RESET         0 // Write 0 to Status reset the device

#define VIRTIO_STATUS_ACKNOWLEDGE   1
#define VIRTIO_STATUS_DRIVER        2
#define VIRTIO_STATUS_DRIVER_OK     4
#define VIRTIO_STATUS_FEATURES_OK   8
#define VIRTIO_STATUS_NEEDS_RESET   64
#define VIRTIO_STATUS_FAILED        128

// Device-independent features bits (modern)
// 0 ~ 31
#define VIRTIO_FEATURE_INDIRECT_DESC        (1<<28)
#define VIRTIO_FEATURE_EVENT_IDX            (1<<29)
// 32 ~ 63
#define VIRTIO_FEATURE_VERSION_1            (1<<(32 - 32))
#define VIRTIO_FEATURE_ACCESS_PLATFORM      (1<<(33 - 32))
#define VIRTIO_FEATURE_RING_PACKED          (1<<(34 - 32))
#define VIRTIO_FEATURE_IN_ORDER             (1<<(35 - 32))
#define VIRTIO_FEATURE_ORDER_PLATFORM       (1<<(36 - 32))
#define VIRTIO_FEATURE_SR_IOV               (1<<(37 - 32))
#define VIRTIO_FEATURE_NOTIFICATION_DATA    (1<<(38 - 32))
#define VIRTIO_FEATURE_NOTIF_CONFIG_DATA    (1<<(39 - 32))
#define VIRTIO_FEATURE_RING_RESET           (1<<(40 - 32))

// Device-independent features bits (legacy)
#define VIRTIO_FEATURE_NOTIFY_ON_EMPTY      (1<<24)
#define VIRTIO_FEATURE_ANY_LAYOUT           (1<<27)
#define VIRTIO_FEATURE_UNUSED               (1<<30)

#define VIRTIO_PAGE_SIZE            4096

enum VirtioBusType
{
    VIRTIO_BUS_TYPE_PCI  = 1,
    VIRTIO_BUS_TYPE_MMIO = 2
};
typedef enum VirtioBusType  VirtioBusType;

enum VirtioDeviceId
{
    /* virtio 1.0 */
    VIRTIO_DEVICE_ID_INVALID        = 0,    /* Invalid device */
    VIRTIO_DEVICE_ID_NET            = 1,    /* Net */
    VIRTIO_DEVICE_ID_BLOCK          = 2,    /* Block */
    VIRTIO_DEVICE_ID_CONSOLE        = 3,    /* Console */
    VIRTIO_DEVICE_ID_ENTROPY        = 4,    /* Entropy */
    VIRTIO_DEVICE_ID_BALLOON        = 5,    /* Balloon */
    VIRTIO_DEVICE_ID_IOMEM          = 6,    /* IO memory */
    VIRTIO_DEVICE_ID_RPMSG          = 7,    /* Remote processor messaging */
    VIRTIO_DEVICE_ID_SCSI           = 8,    /* SCSI */
    VIRTIO_DEVICE_ID_9P             = 9,    /* 9p console */
    VIRTIO_DEVICE_ID_MAC80211_WLAN  = 10,   /* Mac80211 wlan */
    VIRTIO_DEVICE_ID_RPROC_SERIAL   = 11,   /* Remoteproc serial link */
    VIRTIO_DEVICE_ID_CAIF           = 12,   /* CAIF */
    VIRTIO_DEVICE_ID_MEM_BALLOON    = 13,   /* Memory balloon */
    VIRTIO_DEVICE_ID_GPU            = 16,   /* GPU */
    VIRTIO_DEVICE_ID_TIME           = 17,   /* Timer/clock device */
    VIRTIO_DEVICE_ID_INPUT          = 18,   /* Input */
    /* virtio 1.1 */
    VIRTIO_DEVICE_ID_SOCKET         = 19,   /* Socket device */
    VIRTIO_DEVICE_ID_CRYPTO         = 20,   /* Crypto device */
    VIRTIO_DEVICE_ID_SIG_DIS_MOD    = 21,   /* Signal Distribution Module */
    VIRTIO_DEVICE_ID_PSTORE         = 22,   /* Pstore device */
    VIRTIO_DEVICE_ID_IOMMU          = 23,   /* IOMMU device */
    VIRTIO_DEVICE_ID_MEM            = 24,   /* Memory device */
    /* virtio 1.2 */
    VIRTIO_DEVICE_ID_SOUND          = 25,   /* Audio device */
    VIRTIO_DEVICE_ID_FS             = 26,   /* File system device */
    VIRTIO_DEVICE_ID_PMEM           = 27,   /* PMEM device */
    VIRTIO_DEVICE_ID_RPMB           = 28,   /* RPMB device */
    VIRTIO_DEVICE_ID_MAC80211_HWSIM = 29,   /* Mac80211 hwsim wireless simulation device */
    VIRTIO_DEVICE_ID_VIDEO_ENCODER  = 30,   /* Video encoder device */
    VIRTIO_DEVICE_ID_VIDEO_DECODER  = 31,   /* Video decoder device */
    VIRTIO_DEVICE_ID_SCMI           = 32,   /* SCMI device */
    VIRTIO_DEVICE_ID_NITRO_SEC_MOD  = 33,   /* NitroSecureModule */
    VIRTIO_DEVICE_ID_I2C_ADAPTER    = 34,   /* I2C adapter */
    VIRTIO_DEVICE_ID_WATCHDOG       = 35,   /* Watchdog */
    VIRTIO_DEVICE_ID_CAN            = 36,   /* CAN device */
    VIRTIO_DEVICE_ID_DMABUF         = 37,   /* Virtio dmabuf */
    VIRTIO_DEVICE_ID_PARAM_SERV     = 38,   /* Parameter Server */
    VIRTIO_DEVICE_ID_AUDIO_POLICY   = 39,   /* Audio policy device */
    VIRTIO_DEVICE_ID_BT             = 40,   /* Bluetooth device */
    VIRTIO_DEVICE_ID_GPIO           = 41,   /* GPIO device */
    VIRTIO_DEVICE_ID_RDMA           = 42,   /* RDMA device */

    VIRTIO_DEVICE_TYPE_SIZE
};
typedef enum VirtioDeviceId VirtioDeviceId;

typedef void                   (*VirtioConfigChangeHandler)(NX_Device *device);

struct VirtioExtension
{
    VirtioQueue               *queue;
    int                       queue_num;
    VirtioBusType             busType;
    void                      *busExt;
    void                      *devExt;
    NX_Device                 *device; // For reverse lookup
    NX_U32                    flags;
    NX_List                   list;
    NX_IRQ_DelayWork          *irqWorkQueue;
    NX_IRQ_DelayWork          *irqWorkConfig;
    VirtioConfigChangeHandler configChangeNotifier;
};
typedef struct VirtioExtension VirtioExtension;
#define VirtioGetExtension(device) ((VirtioExtension*)(device)->extension)
#define VirtioGetBusExtension(device, type) ((type*)(VirtioGetExtension(device)->busExt))
#define VirtioGetDevExtension(device, type) ((type*)(VirtioGetExtension(device)->devExt))

typedef NX_Error                       (*VirtioDriverInitHandler)(NX_Driver *driver, NX_Device *device);
typedef void                           (*VirtioDriverCleanUpHandler)(NX_Device *device);
struct VirtioDriverTableEntity
{
    const VirtioDeviceId       deviceId;                    /* Static, Driver provide */
    const char                 *driverName;                 /* Static, Driver provide */
    const char                 *deviceNameTemplate;         /* Static, Driver provide */
    const NX_DeviceType        deviceType;                  /* Static, Driver provide */
    NX_DriverOps               *driverOps;                  /* Static, Driver provide */
    VirtioDriverInitHandler    initHandler;                 /* Static, Driver provide */
    VirtioDriverCleanUpHandler cleanUpHandler;              /* Static, Driver provide */
    NX_Driver                  *driver;                     /* Runtime */
    int                        deviceCount;                 /* Runtime */
    NX_List                    deviceListHead;              /* Runtime */
};
typedef struct VirtioDriverTableEntity VirtioDriverTableEntity;

/* Common Config */
enum VirtioCommonConfigItem
{
    VIRTIO_COMMON_CFG_FEATURE_DEVICE,   // VirtioFeature
    VIRTIO_COMMON_CFG_FEATURE_DRIVER,   // VirtioFeature
    VIRTIO_COMMON_CFG_DEVICE_STATUS,    // NX_U8
    VIRTIO_COMMON_CFG_QUEUE_NUM_MAX,    // NX_U16
    VIRTIO_COMMON_CFG_QUEUE_SELECT,     // NX_U16
    VIRTIO_COMMON_CFG_QUEUE_DESC,       // NX_U64
    VIRTIO_COMMON_CFG_QUEUE_DRIVER,     // NX_U64
    VIRTIO_COMMON_CFG_QUEUE_DEVICE,     // NX_U64
    VIRTIO_COMMON_CFG_QUEUE_ENABLE,     // NX_U16, Enable is called Ready in MMIO
    VIRTIO_COMMON_CFG_QUEUE_SIZE,       // NX_U16,
    VIRTIO_COMMON_CFG_CONFIG_GENERATION // NX_U8
};
typedef enum VirtioCommonConfigItem    VirtioCommonConfigItem;

// For VIRTIO_COMMON_CFG_QUEUE_ENABLE
#define VIRTIO_COMMON_CFG_QUEUE_ENABLE_ENABLE 1
#define VIRTIO_COMMON_CFG_QUEUE_ENABLE_DISABLE 0
// For VIRTIO_COMMON_CFG_FEATURE_*
struct VirtioFeature
{
    NX_U32 selector;
    NX_U32 features;
};
typedef struct VirtioFeature           VirtioFeature;

/* ISR */
enum VirtioISRType
{
    VIRTIO_ISR_TYPE_QUEUE,
    VIRTIO_ISR_TYPE_CONFIG
};
typedef enum VirtioISRType             VirtioISRType;

/* Common Config */
void VirtioCommonConfigGet(NX_Device *device, VirtioCommonConfigItem item, void *value);
void VirtioCommonConfigSet(NX_Device *device, VirtioCommonConfigItem item, void *value);
/* Device Config */
void *VirtioGetDeviceConfig(NX_Device *device);
void VirtioReadDeviceConfig(NX_Device *device, NX_Offset offset, void *buf, NX_Size len);
/* Notification Config, VIRTIO_F_NOTIFICATION_DATA should not be negotiated */
// From Device, notifying Driver about Used
void VirtioWaitNotification(NX_Device *device, NX_U16 queue_index, NX_U32 desc_id);
// From Driver, notifying Device about Avail
void VirtioSendNotification(NX_Device *device, NX_U16 queue_index, NX_U32 desc_id);
/* Generic Interrupt Service Routine */
NX_Error VirtioISR(VirtioISRType type, NX_Device *device);
NX_Error VirtioSetupISR(NX_Device *device);
NX_Error VirtioRemoveISR(NX_Device *device);
/* Generic Initialization */
NX_Error VirtioDeviceInitialize(NX_Device *device, NX_U32 *driverFeatures, NX_Size driverFeaturesCount);
NX_Error VirtioDeviceDriverOK(NX_Device *device);
/* Generic Cleanup */
NX_Error VirtioDeviceReset(NX_Device *device);
void VirtioCleanUpDevice(NX_Device *device);

/*
 * Userspace address convert
 */
NX_INLINE void *VirtioVirt2Phy(void *buf)
{
    if (buf == NX_NULL) return buf;
    NX_Process *process = NX_ProcessCurrent();
    if (process)
    {
        // TODO: validate contentious page if buffer is large than one page
        char *pBuf = (void *) NX_VmspaceVirToPhy(&process->vmspace, (NX_Addr) buf);
        if (pBuf == NX_NULL)
        {
            buf = NX_Virt2Phy(buf);
        }
        else
        {
            buf = pBuf;
        }
    }
    else
    {
        buf = NX_Virt2Phy(buf);
    }
    return buf;
}

/*
 * Virtual Address Allocating
 */
#define VIRTIO_VADDR_MASK   0xF0000000;

NX_INLINE NX_Addr VirtioVirtualAddressAllocate(NX_Addr phyAddr)
{
    // Simple solution
    return phyAddr | VIRTIO_VADDR_MASK;
}

#endif // __DRIVERS_VIRTIO_VIRTIO_H__
